import cv2
import tkinter as tk
from tkinter import ttk, messagebox
import threading
import queue

def listCamDevices(cname=None):
    """获取摄像头设备列表"""
    try:
        from PyCameraList.camera_device import list_video_devices
        cameras = list_video_devices()
        camdict = dict(cameras)
        print(camdict)
        if cname:
            finds = []
            for k, v in camdict.items():
                if v.find(cname) != -1:
                    finds.append(k)
            if finds:
                return finds
        return camdict
    except ImportError:
        # 如果PyCameraList不可用，回退到传统方法
        print("PyCameraList不可用，使用传统方法检测摄像头")
        return None

class CameraApp:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("摄像头选择")
        self.root.geometry("300x200")
        
        self.camera_index = None
        self.cap = None
        self.running = False
        self.frame_queue = queue.Queue(maxsize=1)
        
        # 分辨率选项
        self.resolutions = [
            (640, 480),
            (800, 600),
            (1024, 768),
            (1280, 720),
            (1920, 1080)
        ]
        
        # 帧率选项
        self.fps_options = [1, 15, 24, 30]
        
        self.setup_camera_selection()
        
    def setup_camera_selection(self):
        """设置摄像头选择界面"""
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        ttk.Label(main_frame, text="选择摄像头:", font=('Arial', 12)).pack(pady=10)
        
        # 检测可用的摄像头
        self.camera_listbox = tk.Listbox(main_frame, height=6)
        self.camera_listbox.pack(pady=10, fill=tk.BOTH, expand=True)
        
        # 尝试检测摄像头
        self.detect_cameras()
        
        # 确认按钮
        ttk.Button(main_frame, text="打开摄像头", command=self.open_selected_camera).pack(pady=10)
        
    def detect_cameras(self):
        """检测可用的摄像头"""
        available_cameras = []
        
        # 首先尝试使用PyCameraList获取摄像头信息
        cam_dict = listCamDevices()
        
        if cam_dict:
            # 使用PyCameraList的结果
            for index, name in cam_dict.items():
                # 验证摄像头是否真的可用
                cap = cv2.VideoCapture(index)
                if cap.isOpened():
                    available_cameras.append(f"摄像头 {index}: {name}")
                    cap.release()
                else:
                    cap.release()
        else:
            # 回退到传统方法
            for i in range(5):  # 检查前5个摄像头索引
                cap = cv2.VideoCapture(i)
                if cap.isOpened():
                    available_cameras.append(f"摄像头 {i}")
                    cap.release()
                else:
                    cap.release()
                
        if not available_cameras:
            available_cameras = ["未检测到摄像头"]
            
        for camera in available_cameras:
            self.camera_listbox.insert(tk.END, camera)
            
        if available_cameras:
            self.camera_listbox.selection_set(0)
    
    def open_selected_camera(self):
        """打开选中的摄像头"""
        selection = self.camera_listbox.curselection()
        if not selection:
            messagebox.showwarning("警告", "请选择一个摄像头")
            return
            
        selected_text = self.camera_listbox.get(selection[0])
        if "未检测到摄像头" in selected_text:
            messagebox.showwarning("警告", "没有可用的摄像头")
            return
            
        # 提取摄像头索引
        try:
            # 处理新的格式: "摄像头 0: 设备名称"
            if ":" in selected_text:
                # 新格式，提取冒号前的数字
                index_part = selected_text.split(":")[0]
                self.camera_index = int(index_part.split()[-1])
            else:
                # 旧格式: "摄像头 0"
                self.camera_index = int(selected_text.split()[-1])
        except:
            messagebox.showerror("错误", "无法解析摄像头索引")
            return
            
        print(f"选择的摄像头索引: {self.camera_index}")  # 调试信息
        self.root.destroy()  # 关闭选择窗口
        self.start_camera_stream()
    
    def start_camera_stream(self):
        """启动摄像头视频流"""
        print(f"正在尝试打开摄像头 {self.camera_index}...")  # 调试信息
        self.cap = cv2.VideoCapture(self.camera_index)
        
        if not self.cap.isOpened():
            print(f"无法打开摄像头 {self.camera_index}")  # 调试信息
            messagebox.showerror("错误", f"无法打开摄像头 {self.camera_index}")
            return
            
        print("摄像头打开成功，正在启动视频流...")  # 调试信息
        # 设置默认参数
        self.set_camera_parameters(640, 480, 30)
        
        self.running = True
        
        # 启动摄像头线程
        self.camera_thread = threading.Thread(target=self.camera_worker)
        self.camera_thread.daemon = True
        self.camera_thread.start()
        
        # 启动显示线程
        self.display_thread = threading.Thread(target=self.display_worker)
        self.display_thread.daemon = True
        self.display_thread.start()
        
        # 创建新的主窗口用于显示视频流
        self.video_root = tk.Toplevel()
        self.video_root.title(f"摄像头 {self.camera_index} - 预览")
        self.video_root.geometry("800x600")
        
        # 添加说明标签
        info_label = ttk.Label(self.video_root, text="右键点击视频窗口可打开设置", font=('Arial', 10))
        info_label.pack(pady=10)
        
        # 绑定窗口关闭事件
        self.video_root.protocol("WM_DELETE_WINDOW", self.cleanup)
        
        print("视频窗口已创建，启动主循环...")  # 调试信息
        # 启动视频显示窗口的主循环
        self.video_root.mainloop()
        
    def camera_worker(self):
        """摄像头工作线程"""
        while self.running:
            ret, frame = self.cap.read()
            if ret:
                if not self.frame_queue.full():
                    try:
                        self.frame_queue.put_nowait(frame)
                    except:
                        pass
    
    def display_worker(self):
        """显示工作线程"""
        cv2.namedWindow("摄像头预览")
        cv2.setMouseCallback("摄像头预览", self.mouse_callback)
        
        while self.running:
            try:
                frame = self.frame_queue.get(timeout=1)
                cv2.imshow("摄像头预览", frame)
                
                key = cv2.waitKey(1) & 0xFF
                if key == 27:  # ESC键退出
                    self.running = False
                    break
                    
            except queue.Empty:
                continue
                
        self.cleanup()
    
    def mouse_callback(self, event, x, y, flags, param):
        """鼠标回调函数，处理右键点击"""
        if event == cv2.EVENT_RBUTTONDOWN:
            # 使用after方法在主线程中调用设置对话框
            self.root.after(0, self.show_settings_dialog)
    
    def show_settings_dialog(self):
        """显示设置对话框"""
        settings_window = tk.Toplevel()
        settings_window.title("摄像头设置")
        settings_window.geometry("300x250")
        settings_window.resizable(False, False)
        
        # 使设置窗口模态化
        settings_window.transient(self.root)
        settings_window.grab_set()
        
        # 分辨率设置
        ttk.Label(settings_window, text="分辨率:").pack(pady=(20, 5))
        
        resolution_var = tk.StringVar()
        resolution_combo = ttk.Combobox(settings_window, textvariable=resolution_var, state="readonly")
        resolution_combo['values'] = [f"{w} x {h}" for w, h in self.resolutions]
        resolution_combo.pack(pady=5)
        resolution_combo.current(0)
        
        # 帧率设置
        ttk.Label(settings_window, text="帧率:").pack(pady=(20, 5))
        
        fps_var = tk.StringVar()
        fps_combo = ttk.Combobox(settings_window, textvariable=fps_var, state="readonly")
        fps_combo['values'] = self.fps_options
        fps_combo.pack(pady=5)
        fps_combo.current(3)  # 默认选择30fps
        
        # 应用按钮
        def apply_settings():
            try:
                # 解析分辨率
                res_text = resolution_var.get()
                width, height = map(int, res_text.split(' x '))
                fps = int(fps_var.get())
                
                self.set_camera_parameters(width, height, fps)
                settings_window.destroy()
                messagebox.showinfo("成功", "设置已应用")
                
            except Exception as e:
                messagebox.showerror("错误", f"应用设置失败: {str(e)}")
        
        ttk.Button(settings_window, text="应用设置", command=apply_settings).pack(pady=20)
        
        # 居中显示
        settings_window.update_idletasks()
        x = (settings_window.winfo_screenwidth() - settings_window.winfo_width()) // 2
        y = (settings_window.winfo_screenheight() - settings_window.winfo_height()) // 2
        settings_window.geometry(f"+{x}+{y}")
        
        settings_window.wait_window()
    
    def set_camera_parameters(self, width, height, fps):
        """设置摄像头参数"""
        if self.cap and self.cap.isOpened():
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
            self.cap.set(cv2.CAP_PROP_FPS, fps)
    
    def cleanup(self):
        """清理资源"""
        self.running = False
        if self.cap:
            self.cap.release()
        cv2.destroyAllWindows()
        if hasattr(self, 'video_root') and self.video_root:
            self.video_root.destroy()
    
    def run(self):
        """运行应用程序"""
        try:
            self.root.mainloop()
        except KeyboardInterrupt:
            self.cleanup()

if __name__ == "__main__":
    app = CameraApp()
    app.run()